package
{
	import AssetManager;
	import ConfigData;
	import HintSprite;
	import MusicManager;
	import Death;
	import org.flixel.*;

	public class PlayState extends FlxState
	{
		public var doingWin:Boolean = false;
		public var gameRunning:Boolean = false;
		public var gameShouldReset:Boolean = false;
		public var humans:FlxGroup;
		public var deadHumans:FlxGroup;
		public var musicManager:MusicManager = null;
		public var camera:FlxCamera;
		public var background:Background;
		public var ground:Level;
		public var pickupBabyHelpSprite:HintSprite = null;
		public var arrowsSprite:FlxSprite = null;
		public var generationsText:FlxText = null;
		public var death:Death;
		public var heartEmitter:HeartEmitter = null;
		public var tombstoneHud:TombstoneHUD = null;
		public var agingPaused:Boolean = true;
		
		private var _generationsCount:int = 0;
		private var _frozenTimeTimer:FlxTimer = null;
		private var _deadHumansCount:int = 0;
		
		private var _cameraTarget:FlxSprite = null;
		
		override public function create():void
		{
			// Set the background color to light gray (0xAARRGGBB)
			FlxG.bgColor = ConfigData.BACKGROUND_COLOR;

			background = new Background(FlxG.camera);
			ground = new Level(FlxG.height - 10, FlxG.camera);

			pickupBabyHelpSprite = new HintSprite(AssetManager.PickupBabyHint);
			
			// Quick and dirty text and arrows for opening screen
			arrowsSprite = new FlxSprite(80, 35.0, AssetManager.Arrows);
			generationsText = new FlxText(10, 0, 260, ConfigData.GAME_TITLE);
			generationsText.scrollFactor = new FlxPoint(0, 0);
			generationsText.size = 25.0;
			
			musicManager = new MusicManager(this);			
			death = new Death(this);

			// Create Humans in bottom middle
			humans = new FlxGroup();
			humans.add(new Human(this, _generationsCount, FlxG.width / 2, FlxG.height - ConfigData.GROUND_HEIGHT - ConfigData.HUMAN_MIN_HEIGHT * ConfigData.AGE_4_ADULT - ConfigData.INITIAL_HUMAN_ATTENTION_GRABBING_DROP_HEIGHT));
			_cameraTarget = humans.members[0];
			var w:Number = FlxG.width/2;
			var h:Number = FlxG.height/3;
			FlxG.camera.deadzone = new FlxRect((FlxG.width - w) / 2, (FlxG.height - h) / 2 - h * 0.25, w, h);
			humans.members[0].setAge(ConfigData.AGE_4_ADULT);
			humans.members[0].updateGraphics();
			
			deadHumans = new FlxGroup();
			// schedule baby spawn
			
			// Set up heart emitter
			heartEmitter = new HeartEmitter(100, 100, ConfigData.NUMBER_OF_HEARTS_TO_MAKE);
			heartEmitter.lifespan = ConfigData.HEART_LIFE_SPAN;
			heartEmitter.particleClass = HeartParticle;
			heartEmitter.makeParticles(AssetManager.HeartSmall, ConfigData.NUMBER_OF_HEARTS_TO_MAKE, 16, false, 0);
			
			// Set up tombstone hud
			tombstoneHud = new TombstoneHUD(this);
			humans.members[0].setHeartTombstone(tombstoneHud.createHUDHeart(humans.members[0].shirtColorIndex));
			
			// do all adding here for render order
			add(background);
			add(ground);
			add(deadHumans);
			add(death);
			add(humans);
			add(pickupBabyHelpSprite);
			add(arrowsSprite);
			add(tombstoneHud);
			add(heartEmitter);
			add(generationsText);
			
			// Update these things once to facilitate click to start
			background.update();
			//musicManager.update();
		}
		
		public function startGame():void
		{
			FlxG.mouse.hide();
			arrowsSprite.exists = false;
			generationsText.exists = false;
			gameRunning = true;
			_frozenTimeTimer = new FlxTimer;
			_frozenTimeTimer.start(ConfigData.DELAY_BEFORE_FIRST_BABY, 1, spawnInitialBaby);
		}
		
		public function spawnInitialBaby(timer:FlxTimer):void
		{
			makeABaby(humans.members[0]);
			resumeAging();
		}
		
		public function getYoungestHuman():Human 
		{
			var ageToBeat:Number = 100.0;
			var returnHuman:Human = null;
			for each (var human:Human in humans.members)
			{
				if (human != null) {
					if (human.getAge() < ageToBeat) {
						ageToBeat = human.getAge();
						returnHuman = human;
					}
				}
			}
			return returnHuman;
		}
		public function pauseAging():void
		{
			agingPaused = true;
		}
		
		public function resumeAging():void
		{
			agingPaused = false;
		}
		
		public function makeABaby(human:Human):void
		{
			if (human != null)
			{
				var baby:Human = new Human(this, ++_generationsCount, human.x, human.y + human.height - ConfigData.HUMAN_MIN_HEIGHT);
				baby.setParentHuman(human);
				human.setChildHuman(baby);
				human.madeBaby = true;
				humans.add(baby);
				baby.setHeartTombstone(tombstoneHud.createHUDHeart(baby.shirtColorIndex));
				FlxG.play(AssetManager.BabyGiggleSFX);
				if (ConfigData.CAMERA_FOCUS_ON_BABY)
					forceNewTargetForCamera(baby);
			}
		}
		
		public function youLost():void 
		{
			generationsText.exists = true;
			generationsText.text = ConfigData.LOSE_TEXT;
			// We don't want to immediately restart, there's something nice about Death walking away
			gameShouldReset = true;
		}

		public function youWon():void 
		{
			// If we've already won, don't win again...
			if (!death.exists)
				return;
			FlxG.camera.target = null;
			generationsText.exists = true;
			generationsText.text = ConfigData.WIN_TEXT;
			death.exists = false;
			FlxG.play(AssetManager.ChoralSFX);
			for each ( var human1:Human in deadHumans.members)
			{
				var tombstoneSpaceAllotted:int = (FlxG.width / ConfigData.NUMBER_OF_GENERATIONS_TO_WIN);
				var tombstoneOffsetVariable:Number = (tombstoneSpaceAllotted - ConfigData.HUMAN_WIDTH) / 2.0;
				human1.x = FlxG.camera.scroll.x + human1.ID * tombstoneSpaceAllotted + tombstoneOffsetVariable;
				human1.setAge(ConfigData.AGE_4_ADULT);
				human1.updateGraphics();
				human1.y = FlxG.height - ConfigData.GROUND_HEIGHT - human1.height;
				human1.alpha = 0;
			}
			for each ( var human2:Human in humans.members)
			{
				var tombstoneSpaceAllotted1:int = (FlxG.width / ConfigData.NUMBER_OF_GENERATIONS_TO_WIN);
				var tombstoneOffsetVariable1:Number = (tombstoneSpaceAllotted1 - ConfigData.HUMAN_WIDTH) / 2.0;
				human2.x = FlxG.camera.scroll.x + human2.ID * tombstoneSpaceAllotted1 + tombstoneOffsetVariable1;
				//human.setAge(ConfigData.AGE_3_TEEN);
				human2.drag = new FlxPoint(0,0);
				human2.velocity = new FlxPoint(0, 0);
				human2.acceleration = new FlxPoint(0, 0);
				if (human2.getAgeFloored() == ConfigData.AGE_1_INFANT)
					human2.updateGraphics();
				human2.y = FlxG.height - ConfigData.GROUND_HEIGHT - human2.height;
			}
			doingWin = true;

		}
		public function onHumanHitsAgeBoundary(human:Human, age:int):void
		{	
			if (human == null)
			{
				return;
			}
			//trace("age boundary on " + human);
			//trace("Human " + human.ID + " just hit age " + age);
			// if human went past prime, update camera focus as needed
			if (age == ConfigData.AGE_5_OLD_ADULT)
			{
				if (FlxG.camera.target == human)
				{
					pickNewTargetForCamera(human);
				}
			}
			// If the human is dead
			else if (age == ConfigData.AGE_7_DEAD)
			{
				// Humm... is this human the camera's target?
				if (human == FlxG.camera.target)
				{
					pickNewTargetForCamera(human);
				}
				humans.remove(human, true);
				human.immovable = true;
				human.acceleration = new FlxPoint(0, 0);
				human.velocity = new FlxPoint(0, 0);
				human.y = FlxG.height - ConfigData.GROUND_HEIGHT - ConfigData.TOMBSTONE_HEIGHT;
				//tombstoneHud.turnToTombstone(human.ID);
				deadHumans.add(human);
				human.updateGraphics();
				_deadHumansCount++;
				if (_deadHumansCount >= ConfigData.NUMBER_OF_GENERATIONS_TO_WIN)
				{
					youWon();
				}
				else if (humans.length == 0)
				{
					youLost();
				}
			}
		}
		
		public function pickNewTargetForCamera(oldHuman:Human):void 
		{
			// find the next human after the newly past prime one and make it the new camera focus
			for each (var human:Human in humans.members)
			{
				if (human == null)
					continue;
				if (human != oldHuman) {
					FlxG.camera.target = null;
					_cameraTarget = human;
					//trace("trying to follow human: " + human.getAge());
					break;
				}
			}
		}
		
		public function forceNewTargetForCamera(newTarget:Human):void
		{
			FlxG.camera.target = null;
			_cameraTarget = newTarget;
		}
		
		public function emitHeart(colorIndex:int, human:Human, hudIconPos:FlxPoint):void 
		{
			// emit heart at human's position
			heartEmitter.x = human.x;
			heartEmitter.y = human.y;
			// determine relative position of HUD heart icon so particles can move from human to icon
			// adjust hudIconPos into human scroll space first
			var xDiff:Number = (hudIconPos.x + 50.0 + FlxG.camera.scroll.x*human.scrollFactor.x) - human.x;
			var yDiff:Number = (hudIconPos.y + FlxG.camera.scroll.y*human.scrollFactor.y) - human.y;
			heartEmitter.emitColorParticle(ConfigData.AVAILABLE_HEART_COLORS[colorIndex], xDiff, yDiff);
		}
		
		override public function update():void
		{
			// I didn't want to create another whole state, because I want the
			// background graphics to be there and the transition to be smooth
			if (!gameRunning) {
				if (FlxG.mouse.justPressed() || FlxG.keys.justPressed("LEFT") || FlxG.keys.justPressed("RIGHT") || FlxG.keys.justPressed("UP") || FlxG.keys.justPressed("DOWN"))
				{
					startGame();
				} else {
					return;
				}
			}
			
			// After last guy dies, it's nice to see Death walk off and time keep passing, but we need to
			// be ready if they want to play again
			if (gameShouldReset) {
				if (FlxG.mouse.justPressed() || FlxG.keys.justPressed("LEFT") || FlxG.keys.justPressed("RIGHT") || FlxG.keys.justPressed("UP") || FlxG.keys.justPressed("DOWN"))
				{
					FlxG.resetGame();
				}
			}
			
			// Ok, no weird start or end states, update all the objects appropriately
			super.update();
			
			musicManager.update();

			// DEBUG-ONLY: hotkeys to change human age states
			//var humanToChange:Human = null;
			//if (FlxG.keys.F1) // changing human 0
				//humanToChange = humans.members[0];
			//else if (FlxG.keys.F2) // changing human 1
				//humanToChange = humans.members[1];
			//if (humanToChange != null)
			//{
				//if (FlxG.keys.justPressed("ONE")) humanToChange.setAge(ConfigData.AGE_1_INFANT);
				//else if (FlxG.keys.justPressed("TWO")) humanToChange.setAge(ConfigData.AGE_2_TODDLER);
				//else if (FlxG.keys.justPressed("THREE")) humanToChange.setAge(ConfigData.AGE_3_TEEN);
				//else if (FlxG.keys.justPressed("FOUR")) humanToChange.setAge(ConfigData.AGE_4_ADULT);
				//else if (FlxG.keys.justPressed("FIVE")) humanToChange.setAge(ConfigData.AGE_5_OLD_ADULT);
				//else if (FlxG.keys.justPressed("SIX")) humanToChange.setAge(ConfigData.AGE_6_INVALID);
				//else if (FlxG.keys.justPressed("SEVEN")) humanToChange.setAge(ConfigData.AGE_7_DEAD);
			//}
			// DEBUG-ONLY: hotkeys to jump to win state
			//if (FlxG.keys.justPressed("W"))
			//{
				//this.youWon();
			//}
			
			
			if (doingWin)
			{
				if (background.alpha > 0)
				{
					background.alpha -= FlxG.elapsed * .2;
				}
				if (background.timeFactor < 20)
				{
					background.timeFactor += FlxG.elapsed * 6;
				}
				if (ground.alpha > 0)
				{
					ground.alpha -= FlxG.elapsed * .15;
				}
				for each ( var human1:Human in deadHumans.members)
				{
					if (human1.alpha < 1)
					{
						human1.alpha += FlxG.elapsed * .2;
					}
				}
			}
			else if (_cameraTarget != null)
			{
				var targetLeft:Number = _cameraTarget.x + ((_cameraTarget.x > 0)?0.0000001: -0.0000001);
				var targetRight:Number = targetLeft + _cameraTarget.width;
				var deadzone:FlxRect = FlxG.camera.deadzone;
				var leftDeadzoneEdge:Number = FlxG.camera.scroll.x + deadzone.x + ConfigData.CAMERA_FOCUS_DEADZONE;
				var rightDeadzoneEdge:Number = leftDeadzoneEdge + deadzone.width - ConfigData.CAMERA_FOCUS_DEADZONE;
				//trace("deadzone: (" + leftDeadzoneEdge + ", " + rightDeadzoneEdge + "), target = (" + targetLeft + ", " + targetRight + ")");
				if (targetLeft < leftDeadzoneEdge)
				{
					//trace("need to move left!");
					FlxG.camera.scroll.x -= ConfigData.CAMERA_FOCUS_SPEED*FlxG.elapsed;
				}
				else if (targetRight > rightDeadzoneEdge)
				{
					//trace("need to move right!");
					FlxG.camera.scroll.x += ConfigData.CAMERA_FOCUS_SPEED*FlxG.elapsed;
				}
				else 
				{
					//trace("can stop!");
					FlxG.camera.follow(_cameraTarget);
					FlxG.camera.deadzone = deadzone;
					//FlxG.camera.target = _cameraTarget;
					_cameraTarget = null;
				}
			}
			
			// Finally, bump the humans up against the level
			if (!doingWin)
			{
				FlxG.collide(ground, humans);
			}
		}
	}
}
